use clap::Parser;
use std::{fmt, path::Path, str::FromStr};

#[derive(Debug, Copy, Clone, PartialEq)]
pub enum OutputFormat {
    JSON,
    XML,
    CSV,
    YAML,
    TOML,
}

#[derive(Debug, Parser)]
pub struct CsvOpts {
    #[arg(short, long, value_parser = verify_input_file)]
    pub input: String,

    #[arg(short, long)]
    pub output: Option<String>,

    #[arg(short, long, value_parser = parse_format, default_value = "json")]
    pub format: OutputFormat,

    #[arg(short, long, default_value_t = ',')]
    pub delimiter: char,

    #[arg(long, default_value_t = true)]
    pub header: bool,
}

impl fmt::Display for OutputFormat {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}", Into::<&str>::into(self))
    }
}

pub trait OutputTrait {
    fn get_output_formatted(&self) -> String;
}

impl OutputTrait for CsvOpts {
    fn get_output_formatted(&self) -> String {
        if let Some(output) = &self.output {
            output.clone()
        } else {
            format!("assets//test//output.{}", &self.format)
        }
    }
}

fn parse_format(format: &str) -> Result<OutputFormat, anyhow::Error> {
    format.parse()
}

impl From<&OutputFormat> for &'static str {
    fn from(format: &OutputFormat) -> Self {
        match format {
            OutputFormat::JSON => "json",
            OutputFormat::XML => "xml",
            OutputFormat::CSV => "csv",
            OutputFormat::YAML => "yaml",
            OutputFormat::TOML => "toml",
        }
    }
}

impl FromStr for OutputFormat {
    type Err = anyhow::Error;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "json" => Ok(OutputFormat::JSON),
            "xml" => Ok(OutputFormat::XML),
            "csv" => Ok(OutputFormat::CSV),
            "yaml" => Ok(OutputFormat::YAML),
            "toml" => Ok(OutputFormat::TOML),
            v => Err(anyhow::anyhow!("Unsupported output format: {}", v)),
        }
    }
}

fn verify_input_file(input: &str) -> Result<String, String> {
    if input.is_empty() {
        return Err("Input file is empty...".to_string());
    }
    if !Path::new(input).exists() {
        return Err("Input file does not exist...".to_string());
    }
    Ok(input.to_string())
}
